/*
 * main.cpp
 *
 *  Created on: 18/05/2010
 *      Author: Jorge Antonio García Galicia
 */
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <GL/glut.h>

#include "geometry/Vector3D.h"
#include "physics/Particle.h"
#include "physics/EulerIntegrator.h"
#include "physics/RungeKuttaIntegrator.h"
#include "graphics/Sphere.h"
#include "graphics/Cylinder.h"
#include "physics/SpringDamper.h"

#define MUNDO 100.0f   // Tope de las coordenados de el mundo
#define DT 0.1f

using namespace std;
using namespace geometry;
using namespace physics;
using namespace graphics;

void redimensiona(GLsizei w, GLsizei h);
void dibuja(void);
void inicializa(void);
void tecladoEspecial(int key, int mx, int my);
void funcionTiempo(void);
void moverEscena(void);
void teclado(unsigned char key, int mx, int my);
float grad2Radians(float);

//Objetos fisicos
Particle particulas[5];
SpringDamper *resortes;

//Objetos graficos
Sphere puntos[5];
Cylinder tubos[4];


int main (int argc, char* argv[]) {

	glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(400, 400);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("Pequeño ejemplo de Masas y resortes");
    inicializa();
    /** Funciones de callback */
    glutSpecialFunc(tecladoEspecial);
    glutDisplayFunc(dibuja);
    glutReshapeFunc(redimensiona);
    glutKeyboardFunc(teclado);
    glutIdleFunc(funcionTiempo);
    glutMainLoop();

    return 0;
}

void inicializa(void)
{
	int i;
	float x0 = -2.0 * MUNDO / 3.0;
	float dx = MUNDO / 3.0;


     glClearColor(0.0, 0.0, 0.0, 0.0);
     glEnable(GL_DEPTH_TEST);
     Vector3D cero(0, 0, 0);
     Vector3D amarillo(1, 1, 0);
     Vector3D rojo(1, 0, 0);
     glLineWidth(1.0);

     //Inicializamos las particulas
     for (i = 0; i < 5; i++) {
    	 particulas[i].setMass(1.0);
    	 particulas[i].setForce(cero);
    	 particulas[i].setVelocity(cero);
    	 particulas[i].setPosition(Vector3D(x0, 0, 0));
    	 x0 += dx;
     }

     //Ahora inicializamos los resortes
     resortes = new SpringDamper[4];
     for (i = 0; i < 4; i++) {
    	 resortes[i] = SpringDamper(&particulas[i], &particulas[i + 1], 0.85, 0.9);
     }

     //Ahora los objetos graficos
     for (i = 0; i < 5; i++) {
    	 puntos[i].setColor(amarillo);
    	 puntos[i].setRadio(3.0f);
     }

     for (i = 0; i < 4; i++) {
    	 tubos[i].setColor(rojo);
    	 tubos[i].setRadio(1.0);
	 }

     //Fijamos los dos extremos
     particulas[0].setFixed();
     particulas[4].setFixed();

     //Prendemos las luces
     glEnable(GL_LIGHTING);
     glEnable(GL_LIGHT0);
     glEnable(GL_COLOR_MATERIAL);
     glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
}

void redimensiona(GLsizei w, GLsizei h) {
    GLfloat aspectRatio;

    if (h == 0)
       h = 1;

    glViewport(0, 0, w, h);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    aspectRatio = (GLfloat)w / (GLfloat)h;

    if (w <= h)
       glOrtho(-MUNDO, MUNDO, -MUNDO / aspectRatio, MUNDO / aspectRatio, -MUNDO, MUNDO);
    else
       glOrtho(-MUNDO * aspectRatio, MUNDO * aspectRatio, -MUNDO, MUNDO, -MUNDO, MUNDO);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void tecladoEspecial(int key, int mx, int my) {
	switch(key) {
		case GLUT_KEY_LEFT:

		break;

		case GLUT_KEY_RIGHT:

		break;

		case GLUT_KEY_UP:

		break;

		case GLUT_KEY_DOWN:

		break;

		case GLUT_KEY_HOME:

		break;
	}

	glutPostRedisplay();
}

void funcionTiempo() {
	glutPostRedisplay();
}

void dibuja () {
	int i;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	moverEscena();

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	//Ponemos los objetos graficos en donde estan los fisicos
	for (i = 0; i < 5; i++) {
		puntos[i].setCenter(particulas[i].getPosition());
	}

	for (i = 0; i < 4; i++) {
		tubos[i].setEdges(particulas[i].getPosition(), particulas[i + 1].getPosition());
	}

	//Dibujamos todos los objetos graficos
	for (i = 0; i < 5; i++) {
		puntos[i].draw();
	}

	for (i = 0; i < 4; i++) {
		tubos[i].draw();
	}


	glutSwapBuffers();

}

void moverEscena() {
	EulerIntegrator e(DT);
	int i;

	//Vaciamos la fuerza
	for (i = 0; i < 5; i++) {
		particulas[i].emptyForce();
	}

	//Acumulamos la gravedad
	Vector3D g(0, -9.8, 0);
	for (i = 0; i < 5; i++) {
		particulas[i].addForce(g);
	}

	//Acumulamos los resortes amortiguadores
	for (i = 0; i < 4; i++) {
		resortes[i].acomulateForce();
	}


	//Intergramos
	for (i = 0; i < 5; i++) {
		e.integrate(particulas[i]);
	}

}

void teclado(unsigned char key, int mx, int my) {

	switch(key) {

	case 27:
		exit(0);
		break;

	}

	glutPostRedisplay();
}

float grad2Radians (float angleDeg) {
	return 0.017453293f * angleDeg;
}
